`include "mycpu.h"

module id_stage(
    input                          clk           ,
    input                          reset         ,
    //allowin
    input                          es_allowin    ,
    output                         ds_allowin    ,
    //from fs
    input                          fs_to_ds_valid,
    input  [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus  ,
    input                          ws_flush      ,
    //to es
    output                         ds_to_es_valid,
    output [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus  ,
    //to fs
    output [`DS_TO_FS_BUS_WD -1:0] ds_to_fs_bus  ,
    //to rf: for write back
    input  [`WS_TO_RF_BUS_WD -1:0] ws_to_rf_bus  ,
    //������ˮ�߳�ͻ
    input  [`ES_CONFLICT_BUS_WD -1:0] es_conflict_bus,
    input  [`MS_CONFLICT_BUS_WD -1:0] ms_conflict_bus
);

reg         ds_valid   ;
wire        ds_ready_go;

wire [31                 :0] fs_pc;
reg  [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus_r;
assign fs_pc = fs_to_ds_bus[31:0];

/* privileged instruction  and exceptions*/
wire excep;
wire [7:0] c0_addr;
wire fs_ex;
wire ds_ex;
wire ds_ri;
wire [4:0] fs_excode;
wire [4:0] ds_excode;
wire ov_possible;
wire tlb_refill;
reg  bd;
wire ds_retake;
wire inst_retake;


wire [31:0] ds_inst;
wire [31:0] ds_pc  ;
assign {tlb_refill,
        inst_retake,
        fs_ex,
        fs_excode,
        ds_inst,
        ds_pc  } = fs_to_ds_bus_r;

wire        rf_we   ;
wire [ 4:0] rf_waddr;
wire [31:0] rf_wdata;
assign {rf_we   ,  //37:37
        rf_waddr,  //36:32
        rf_wdata   //31:0
       } = ws_to_rf_bus;

wire        branch;
wire        br_stall;
wire        br_taken;
wire [31:0] br_target;
wire        switch; 

wire [19:0] alu_op;
wire        alu_reg;
wire        alu_imm;
wire        alu_immu;
wire        alu_sa;
wire        src1_is_sa;
wire        src1_is_pc;
wire        src1_is_null;
wire        src2_is_imm;
wire        src2_is_immu;
wire        src2_is_8;
wire        src2_is_null;  
wire        res_from_mem;
wire        gr_we;
wire [ 4:0] dest;
wire [15:0] imm;
wire [31:0] rs_value;
wire [31:0] rt_value;

wire [ 5:0] op;
wire [ 4:0] rs;
wire [ 4:0] rt;
wire [ 4:0] rd;
wire [ 4:0] sa;
wire [ 5:0] func;
wire [25:0] jidx;
wire [ 2:0] sel;
wire [63:0] op_d;
wire [31:0] rs_d;
wire [31:0] rt_d;
wire [31:0] rd_d;
wire [31:0] sa_d;
wire [63:0] func_d;

//add load-store instructions
wire        load;
wire [6:0]  load_type;
wire        store;
wire [4:0]  store_type;

wire        inst_sllv;
wire        inst_srlv;
wire        inst_srav;
wire        inst_mfhi;
wire        inst_mthi;
wire        inst_mflo;
wire        inst_mtlo;
wire        inst_mult;
wire        inst_multu;
wire        inst_div;
wire        inst_divu;
wire        inst_add;
wire        inst_addu;
wire        inst_sub;
wire        inst_subu;
wire        inst_slt;
wire        inst_sltu;
wire        inst_and;
wire        inst_or;
wire        inst_xor;
wire        inst_nor;
wire        inst_sll;
wire        inst_srl;
wire        inst_sra;
wire        inst_addi;  
wire        inst_addiu;
wire        inst_slti;
wire        inst_sltiu;
wire        inst_andi;
wire        inst_ori;
wire        inst_xori;
wire        inst_lui;
wire        inst_lw;
wire        inst_lb;
wire        inst_lbu;
wire        inst_lh;
wire        inst_lhu;
wire        inst_lwl;
wire        inst_lwr;
wire        inst_sb;
wire        inst_sh;
wire        inst_sw;
wire        inst_swl;
wire        inst_swr;
wire        inst_bltz;
wire        inst_bgez;
wire        inst_bltzal;
wire        inst_bgezal;
wire        inst_j;
wire        inst_jal;
wire        inst_beq;
wire        inst_bne;
wire        inst_blez;
wire        inst_bgtz;
wire        inst_jr;
wire        inst_jalr;
wire        inst_eret;
wire        inst_mfc0;
wire        inst_mtc0;
wire        inst_syscall;
wire        inst_break;
wire        inst_tlbp;
wire        inst_tlbwi;
wire        inst_tlbr;

wire        dst_is_r31;  
wire        dst_is_rt;   

wire [ 4:0] rf_raddr1;
wire [31:0] rf_rdata1;
wire [ 4:0] rf_raddr2;
wire [31:0] rf_rdata2;

wire        rs_eq_rt;
wire        rs_lt_zero;
wire        rs_eq_zero;


/*��ˮ�߳�ͻ*/
wire [37:0] ws_conflict_bus;
wire        rs_conflict_possible;//����ָ���Ƿ��п��ܷ�����ͻ
wire        rt_conflict_possible;
wire        rs_conflict;
wire        rs_es_conflict;
wire        rs_ms_conflict;
wire        rs_ws_conflict;
wire        rt_conflict;
wire        rt_es_conflict;
wire        rt_ms_conflict;
wire        rt_ws_conflict;
wire        block;      //���뼶��ָ���������ִ�м���loadָ�ֻ������һ��
assign      ws_conflict_bus = ws_to_rf_bus;
assign rs_conflict_possible  =  ~src1_is_sa & ~src1_is_null & ~inst_jal & ~(rs==5'd0);
assign rt_conflict_possible  =  ~src2_is_8  & ~src2_is_null & (~dst_is_rt | inst_lwl | inst_lwr) & ~(rt==5'd0);
assign rs_es_conflict = rs_conflict_possible & es_conflict_bus[5] & (es_conflict_bus[4:0]==rs);
assign rs_ms_conflict = rs_conflict_possible & ms_conflict_bus[37] & (ms_conflict_bus[36:32]==rs);
assign rs_ws_conflict = rs_conflict_possible & ws_conflict_bus[37] & (ws_conflict_bus[36:32]==rs);
assign rt_es_conflict = rt_conflict_possible & es_conflict_bus[5] & (es_conflict_bus[4:0]==rt);
assign rt_ms_conflict = rt_conflict_possible & ms_conflict_bus[37] & (ms_conflict_bus[36:32]==rt);
assign rt_ws_conflict = rt_conflict_possible & ws_conflict_bus[37] & (ws_conflict_bus[36:32]==rt);
assign rs_conflict = rs_es_conflict | rs_ms_conflict | rs_ws_conflict;
assign rt_conflict = rt_es_conflict | rt_ms_conflict | rt_ws_conflict;
assign block = (rs_es_conflict | rt_es_conflict)
            || (rs_ms_conflict | rt_ms_conflict) & (!ms_conflict_bus[38] | ms_conflict_bus[39]);

assign ds_to_fs_bus = {ds_retake, br_stall, br_taken, br_target};

assign ds_to_es_bus = {inst_retake ,  //178:178
                       inst_tlbr   ,  //177:177
                       inst_tlbwi  ,  //176:176
                       inst_tlbp   ,  //175:175
                       ov_possible ,  //174:174
                       bd          ,  //173:173
                       ds_excode   ,  //172:168
                       ds_ex       ,  //167:167
                       inst_eret   ,  //166:166
                       inst_mfc0   ,  //165:165
                       inst_mtc0   ,  //164:164
                       c0_addr     ,  //163:156
                       store_type  ,  //155:151
                       load_type   ,  //150:144
                       alu_op      ,  //143:124
                       src1_is_sa  ,  //123:123
                       src1_is_pc  ,  //122:122
                       src2_is_imm ,  //121:121
                       src2_is_immu,  //120:120
                       src2_is_8   ,  //119:119
                       gr_we       ,  //118:118
                       tlb_refill  ,  //117:117
                       dest        ,  //116:112
                       imm         ,  //111:96
                       rs_value    ,  //95 :64
                       rt_value    ,  //63 :32
                       ds_pc          //31 :0
                      };

assign ds_ready_go    = !block;
assign ds_allowin     = !ds_valid || ds_ready_go && es_allowin;
assign ds_to_es_valid = ds_valid && ds_ready_go;
always @(posedge clk) begin
    if (reset)
        ds_valid <= 1'b0;
    else if (ws_flush)
        ds_valid <= 1'b0;
    else if (ds_allowin)
        ds_valid <= fs_to_ds_valid;
        
    if (reset || ws_flush)
        fs_to_ds_bus_r <= 73'd0;
    else if (fs_to_ds_valid && ds_allowin)
        fs_to_ds_bus_r <= fs_to_ds_bus;
end

assign op   = ds_inst[31:26];
assign rs   = ds_inst[25:21];
assign rt   = ds_inst[20:16];
assign rd   = ds_inst[15:11];
assign sa   = ds_inst[10: 6];
assign func = ds_inst[ 5: 0];
assign imm  = ds_inst[15: 0];
assign jidx = ds_inst[25: 0];
assign sel  = ds_inst[ 2: 0];

decoder_6_64 u_dec0(.in(op  ), .out(op_d  ));
decoder_6_64 u_dec1(.in(func), .out(func_d));
decoder_5_32 u_dec2(.in(rs  ), .out(rs_d  ));
decoder_5_32 u_dec3(.in(rt  ), .out(rt_d  ));
decoder_5_32 u_dec4(.in(rd  ), .out(rd_d  ));
decoder_5_32 u_dec5(.in(sa  ), .out(sa_d  ));

assign inst_sll    = op_d[6'h00] & func_d[6'h00] & rs_d[5'h00];
assign inst_srl    = op_d[6'h00] & func_d[6'h02] & rs_d[5'h00];
assign inst_sra    = op_d[6'h00] & func_d[6'h03] & rs_d[5'h00];
assign inst_sllv   = op_d[6'h00] & func_d[6'h04] & sa_d[5'h00];
assign inst_srlv   = op_d[6'h00] & func_d[6'h06] & sa_d[5'h00];
assign inst_srav   = op_d[6'h00] & func_d[6'h07] & sa_d[5'h00];
assign inst_jr     = op_d[6'h00] & func_d[6'h08] & rt_d[5'h00] & rd_d[5'h00] & sa_d[5'h00];
assign inst_jalr   = op_d[6'h00] & func_d[6'h09] & rt_d[5'h00];
assign inst_syscall= op_d[6'h00] & func_d[6'h0c];
assign inst_break  = op_d[6'h00] & func_d[6'h0d];
assign inst_mfhi   = op_d[6'h00] & func_d[6'h10] & rs_d[5'h00] & rt_d[5'h00] & sa_d[5'h00];
assign inst_mthi   = op_d[6'h00] & func_d[6'h11] & rt_d[5'h00] & rd_d[5'h00] & sa_d[5'h00];
assign inst_mflo   = op_d[6'h00] & func_d[6'h12] & rs_d[5'h00] & rt_d[5'h00] & sa_d[5'h00];
assign inst_mtlo   = op_d[6'h00] & func_d[6'h13] & rt_d[5'h00] & rd_d[5'h00] & sa_d[5'h00];
assign inst_mult   = op_d[6'h00] & func_d[6'h18] & sa_d[5'h00];
assign inst_multu  = op_d[6'h00] & func_d[6'h19] & sa_d[5'h00];
assign inst_div    = op_d[6'h00] & func_d[6'h1a] & sa_d[5'h00];
assign inst_divu   = op_d[6'h00] & func_d[6'h1b] & sa_d[5'h00];
assign inst_add    = op_d[6'h00] & func_d[6'h20] & sa_d[5'h00];
assign inst_addu   = op_d[6'h00] & func_d[6'h21] & sa_d[5'h00];
assign inst_sub    = op_d[6'h00] & func_d[6'h22] & sa_d[5'h00];
assign inst_subu   = op_d[6'h00] & func_d[6'h23] & sa_d[5'h00];
assign inst_slt    = op_d[6'h00] & func_d[6'h2a] & sa_d[5'h00];
assign inst_sltu   = op_d[6'h00] & func_d[6'h2b] & sa_d[5'h00];
assign inst_and    = op_d[6'h00] & func_d[6'h24] & sa_d[5'h00];
assign inst_or     = op_d[6'h00] & func_d[6'h25] & sa_d[5'h00];
assign inst_xor    = op_d[6'h00] & func_d[6'h26] & sa_d[5'h00];
assign inst_nor    = op_d[6'h00] & func_d[6'h27] & sa_d[5'h00];
assign inst_bltz   = op_d[6'h01] & rt_d[5'h00];
assign inst_bgez   = op_d[6'h01] & rt_d[5'h01];
assign inst_bltzal = op_d[6'h01] & rt_d[5'h10];
assign inst_bgezal = op_d[6'h01] & rt_d[5'h11];
assign inst_j      = op_d[6'h02];
assign inst_jal    = op_d[6'h03];
assign inst_beq    = op_d[6'h04];
assign inst_bne    = op_d[6'h05];
assign inst_blez   = op_d[6'h06];
assign inst_bgtz   = op_d[6'h07];
assign inst_addi   = op_d[6'h08];
assign inst_addiu  = op_d[6'h09];
assign inst_slti   = op_d[6'h0a];
assign inst_sltiu  = op_d[6'h0b];
assign inst_andi   = op_d[6'h0c];
assign inst_ori    = op_d[6'h0d];
assign inst_xori   = op_d[6'h0e];
assign inst_lui    = op_d[6'h0f] & rs_d[5'h00];
assign inst_mfc0   = op_d[6'h10] & rs_d[5'h00];
assign inst_mtc0   = op_d[6'h10] & rs_d[5'h04];
assign inst_tlbr   = op_d[6'h10] & rs_d[5'h10] & rt_d[5'h00] & rd_d[5'h00] & sa_d[5'h00] & func_d[6'h01];
assign inst_tlbwi  = op_d[6'h10] & rs_d[5'h10] & rt_d[5'h00] & rd_d[5'h00] & sa_d[5'h00] & func_d[6'h02];
assign inst_tlbp   = op_d[6'h10] & rs_d[5'h10] & rt_d[5'h00] & rd_d[5'h00] & sa_d[5'h00] & func_d[6'h08];
assign inst_eret   = op_d[6'h10] & rs_d[5'h10] & rt_d[5'h00] & rd_d[5'h00] & sa_d[5'h00] & func_d[6'h18];
assign inst_lb     = op_d[6'h20];
assign inst_lh     = op_d[6'h21];
assign inst_lwl    = op_d[6'h22];
assign inst_lw     = op_d[6'h23];
assign inst_lbu    = op_d[6'h24];
assign inst_lhu    = op_d[6'h25];
assign inst_lwr    = op_d[6'h26];
assign inst_sb     = op_d[6'h28];
assign inst_sh     = op_d[6'h29];
assign inst_swl    = op_d[6'h2a];
assign inst_sw     = op_d[6'h2b];
assign inst_swr    = op_d[6'h2e];
assign alu_reg = inst_add | inst_addu | inst_sub | inst_subu | inst_slt | inst_sltu | inst_and | inst_nor | inst_or | inst_xor | inst_sllv | inst_srlv | inst_srav | inst_mult | inst_multu | inst_div | inst_divu | inst_mfhi | inst_mflo | inst_mthi | inst_mtlo;
assign alu_imm = inst_addi | inst_addiu | inst_slti  | inst_sltiu | inst_lui;
assign alu_immu = inst_andi | inst_ori   | inst_xori;
assign alu_sa = inst_sll  | inst_srl   | inst_sra;
assign load = inst_lb | inst_lbu | inst_lh | inst_lhu | inst_lw | inst_lwl | inst_lwr;
assign store = inst_sb | inst_sh | inst_sw | inst_swl | inst_swr;
assign branch = inst_bltz | inst_bgez | inst_bltzal | inst_bgezal | inst_beq | inst_bne | inst_blez | inst_bgtz;
assign switch = branch | inst_j | inst_jal | inst_jalr | inst_jr;
assign excep = inst_break | inst_syscall | inst_eret | inst_tlbp | inst_tlbwi | inst_tlbr;
assign ds_ri = !(load | store | switch | alu_reg | alu_imm | alu_immu | alu_sa | excep | inst_mfc0 | inst_mtc0);

assign alu_op[ 0] = inst_add | inst_addu | inst_addi | inst_addiu | load | store | inst_jal | inst_jalr | inst_bltzal | inst_bgezal;
assign alu_op[ 1] = inst_sub | inst_subu;
assign alu_op[ 2] = inst_slt | inst_slti;
assign alu_op[ 3] = inst_sltu | inst_sltiu;
assign alu_op[ 4] = inst_and | inst_andi;
assign alu_op[ 5] = inst_nor;
assign alu_op[ 6] = inst_or | inst_ori;
assign alu_op[ 7] = inst_xor | inst_xori;
assign alu_op[ 8] = inst_sll | inst_sllv;
assign alu_op[ 9] = inst_srl | inst_srlv;
assign alu_op[10] = inst_sra | inst_srav;
assign alu_op[11] = inst_lui;
assign alu_op[12] = inst_mult;
assign alu_op[13] = inst_multu;
assign alu_op[14] = inst_div;
assign alu_op[15] = inst_divu;
assign alu_op[16] = inst_mfhi;
assign alu_op[17] = inst_mflo;
assign alu_op[18] = inst_mthi;
assign alu_op[19] = inst_mtlo;

assign src1_is_sa   = alu_sa;
assign src1_is_pc   = inst_jal  | inst_jalr  | inst_bltzal| inst_bgezal;
assign src1_is_null = inst_j    | inst_mfc0  | inst_mtc0  | excep;
assign src2_is_imm  = alu_imm   | load       | store;
assign src2_is_immu = alu_immu;
assign src2_is_8    = inst_jal  | inst_jalr  | inst_bltzal| inst_bgezal;
assign src2_is_null = inst_bltz | inst_bgez  | inst_j     | inst_bltzal| inst_bgezal | excep;
assign res_from_mem = load;
assign dst_is_r31   = inst_jal  | inst_bltzal| inst_bgezal;
assign dst_is_rt    = alu_imm | alu_immu | load | inst_mfc0;
assign gr_we        = load | alu_reg | alu_imm | alu_immu | alu_sa | inst_mfc0 | inst_bltzal | inst_bgezal | inst_jal | inst_jalr; 

assign dest         = dst_is_r31 ? 5'd31 :
                      dst_is_rt  ? rt    : 
                                   rd;

assign load_type[0]  = inst_lb;
assign load_type[1]  = inst_lbu;
assign load_type[2]  = inst_lh;
assign load_type[3]  = inst_lhu;
assign load_type[4]  = inst_lw;
assign load_type[5]  = inst_lwl;
assign load_type[6]  = inst_lwr;
assign store_type[0] = inst_sb;
assign store_type[1] = inst_sh;
assign store_type[2] = inst_sw;
assign store_type[3] = inst_swl;
assign store_type[4] = inst_swr;

assign rf_raddr1 = rs;
assign rf_raddr2 = rt;
regfile u_regfile(
    .clk    (clk      ),
    .raddr1 (rf_raddr1),
    .rdata1 (rf_rdata1),
    .raddr2 (rf_raddr2),
    .rdata2 (rf_rdata2),
    .we     (rf_we    ),
    .waddr  (rf_waddr ),
    .wdata  (rf_wdata )
    );

assign rs_value = ~rs_conflict? rf_rdata1:
                  rs_ms_conflict? ms_conflict_bus[31:0]: ws_conflict_bus[31:0];
assign rt_value = ~rt_conflict? rf_rdata2:
                  rt_ms_conflict? ms_conflict_bus[31:0]: ws_conflict_bus[31:0];

assign rs_eq_rt   = (rs_value == rt_value);
assign rs_eq_zero = (rs_value == 32'd0);
assign rs_lt_zero = rs_value[31];
assign br_stall = block & (branch | inst_jr | inst_jalr);
assign br_taken = (   inst_bltz  &&  rs_lt_zero
                   || inst_bgez  && !rs_lt_zero
                   || inst_bltzal&&  rs_lt_zero
                   || inst_bgezal&& !rs_lt_zero
                   || inst_beq   &&  rs_eq_rt
                   || inst_bne   && !rs_eq_rt
                   || inst_blez  && (rs_lt_zero | rs_eq_zero)
                   || inst_bgtz  &&!(rs_lt_zero | rs_eq_zero)
                   || inst_j
                   || inst_jal
                   || inst_jr
                   || inst_jalr) && ds_to_es_valid;
assign br_target = {32{branch}} & (ds_pc + {{14{imm[15]}}, imm[15:0], 2'b0} + 4)
                  |{32{inst_jr || inst_jalr}} & rs_value
                  |{32{inst_jal || inst_j}} & {ds_pc[31:28], jidx[25:0], 2'b0};

/* priviledged instructions and exceptions */
assign c0_addr = {rd, sel};
assign ds_ex = fs_ex || inst_syscall || inst_break || ds_ri;
assign ds_excode = fs_ex? fs_excode: 
                   ds_ri? `EX_RI : {5{inst_syscall}} & `EX_SYS | {5{inst_break}} & `EX_BP;
always @(posedge clk) begin
    if(switch && fs_to_ds_valid && ds_allowin)
        bd <= 1'b1;
    else if(ds_to_es_valid && es_allowin)
        bd <= 1'b0;
end
assign ov_possible = inst_add | inst_addi | inst_sub;
assign ds_retake = (inst_tlbr || inst_tlbwi) && ds_valid && !ds_ex;

endmodule
